package net.jxtz.jxtzos.cdfile;


import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.jxtz.jxtzos.entity.cdfile.CdFile;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author a123
 */

@Component
@Slf4j
public final class FileServiceImpl {

    @Value("${cd-file-path}")
    private String rootPath;

    private final static String ROOT_FILE_NAME = "全部文件";

    private final static Integer BUFFER_SIZE = 5 * 1024;

    public List<CdFile> initFileList(String filePath){
        if (ROOT_FILE_NAME.equals(filePath) || filePath == null){
            filePath = this.rootPath;
        }
        File file = new File(filePath);
        File[] files = file.listFiles();
        List<CdFile> list = new ArrayList<>();
        for (File file1 : files) {
            CdFile cdFile = new CdFile();
            String name = file1.getName();
            cdFile.setFileName(name);
            cdFile.setFilePath(file1.getPath());
            if (file1.isDirectory()) {
                cdFile.setFileStatus(1);
            }else {
                cdFile.setFileStatus(0);
            }
            // 字节（b），转成 Mb
            Double size = (double) file1.length() / 1024 / 1024;
            BigDecimal bigDecimal = new BigDecimal(size + "");
            double v = bigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            cdFile.setFileSize(v);
            cdFile.setLastModified(file1.lastModified() / 1000);

            list.add(cdFile);
        }

        // 排序
        list = list.stream().sorted(Comparator.comparing(CdFile::getFileName)).collect(Collectors.toList());
        return list;
    }

    /**
     * 上传文件夹
     * @param rootFilePath 根目录
     * @param files 文件夹
     */
    @SneakyThrows
    public void addFiles(String rootFilePath, MultipartFile[] files){
        if (rootFilePath.equals(ROOT_FILE_NAME)){
            rootFilePath = this.rootPath;
        }

        for (MultipartFile file : files) {
            String name = "/" + file.getOriginalFilename();
            File f = new File(rootFilePath + name);
            FileUtils.copyInputStreamToFile(file.getInputStream(), f);
        }
    }

    /**
     * 上传单个文件
     * @param rootFilePath 根目录
     * @param file 文件
     */
    @SneakyThrows
    public void addFile(String rootFilePath, MultipartFile file){
        if (rootFilePath.equals(ROOT_FILE_NAME)){
            rootFilePath = this.rootPath;
        }

        File f = new File(rootFilePath + "/" +file.getOriginalFilename());
        file.transferTo(f);
    }

    /**
     * 新建文件夹
     * @param cdFile 新增对象
     * @return 是否创建成功
     */
    @SneakyThrows
    public boolean createNewFolder(CdFile cdFile){
        String fileName = cdFile.getFileName();
        String filePath = URLDecoder.decode(cdFile.getFilePath(), "UTF-8");
        if (filePath.equals(ROOT_FILE_NAME)){
            filePath = this.rootPath;
        }
        log.info(filePath + fileName);
        File file = new File(filePath + "/" + fileName);
        if (file.exists()){
            return false;
        }else {
            return file.mkdir();
        }
    }

    /**
     * 搜索文件信息
     * @param rootFilePath 根目录
     * @param searchContent 关键字
     * @return 搜索到的文件信息集合
     */
    public List<CdFile> searchFiles(String rootFilePath, String searchContent){
        List<CdFile> list = initFileList(rootFilePath);
        return list.stream().filter(cdFile -> cdFile.getFileName().contains(searchContent)).collect(Collectors.toList());
    }

    /**
     * 批量删除文件
     * @param cdFiles 多文件
     */
    @SneakyThrows
    public void deleteFiles(List<CdFile> cdFiles){
        log.info(cdFiles.toString());
        for (CdFile cdFile : cdFiles) {
            if (cdFile.getFilePath() != null && !"".equals(cdFile.getFilePath())){
                String filePath = cdFile.getFilePath();
                File file = new File(filePath);
                if (file.exists()){
                    boolean b = FileUtils.deleteQuietly(file);
                    log.info("删除 " + filePath + " " + b);
                }
            }
        }
    }

    /**
     * 重命名文件
     * @param cdFile 原文件
     * @param newFileName 新名称
     * @return 是否重命名成功
     */
    public boolean setFileName(CdFile cdFile, String newFileName){
        File oldFile = new File(cdFile.getFilePath());
        if (cdFile.getFileStatus() == 1){
            String filePath = cdFile.getFilePath().substring(0, cdFile.getFilePath().lastIndexOf("/"));
            return oldFile.renameTo(new File(filePath + "/" + newFileName));
        }else {
            String filePath = cdFile.getFilePath().substring(0, cdFile.getFilePath().lastIndexOf("/"));
            String fileName = cdFile.getFileName();
            String fileNameSuffix = fileName.substring(fileName.lastIndexOf("."));
            return oldFile.renameTo(new File(filePath + "/" + newFileName + fileNameSuffix));
        }
    }

    /**
     * 下载zip文件
     * 如果是文件夹则压缩成 zip 格式下载
     * @param cdFile 文件对象
     */
    public void downloadFile(CdFile cdFile, OutputStream os, boolean keepDirStructure) {
        // 文件夹
        File file = new File(cdFile.getFilePath());
        if (cdFile.getFileStatus() == 1){
            try( ZipOutputStream zos = new ZipOutputStream(os)){
                compress(file, zos, file.getName() + ".zip", keepDirStructure);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else {
            // 一般文件
            fileDownload(cdFile, os);
        }
    }

    /**
     * 一般文件下载
     * @param file 文件对象
     */
    private static void fileDownload(CdFile file, OutputStream os){
        File f = new File(file.getFilePath());
        byte[] bytes = new byte[BUFFER_SIZE];
        int len = 0;
        try(FileInputStream fis = new FileInputStream(f);) {
            while ((len = fis.read(bytes)) != -1){
                os.write(bytes, 0, len);
            }
            os.flush();
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 批量下载文件打包成zip
     * @param cdFiles 批量文件
     * @param os 输出流
     */
    public void downloadFiles(List<CdFile> cdFiles, OutputStream os){
        try(ZipOutputStream zos = new ZipOutputStream(os);){
            for (CdFile cdFile : cdFiles) {
//                byte[] bytes = new byte[BUFFER_SIZE];
//                zos.putNextEntry(new ZipEntry(cdFile.getFileName()));
//                int len = 0;
                if (cdFile.getFileStatus() == 0){
//                    try(FileInputStream fis = new FileInputStream(cdFile.getFilePath());){
//                        while ((len = fis.read(bytes)) != -1){
//                            zos.write(bytes, 0, len);
//                        }
//                        zos.closeEntry();
//                    }
                    compress(new File(cdFile.getFilePath()), zos, cdFile.getFileName(), false);
                }else {
                    compress(new File(cdFile.getFilePath()), zos, cdFile.getFileName() + "/", true);
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    /**
     * 递归压缩zip，单个文件夹
     * @param file 文件对象
     * @param zos zip输出流
     * @param name 压缩文件名称
     * @param keepDirStructure 是否保留源文件结构
     */
    @SneakyThrows
    private static void compress(File file, ZipOutputStream zos, String name, boolean keepDirStructure){
        byte[] bytes = new byte[BUFFER_SIZE];
        if (file.isFile()){
            zos.putNextEntry(new ZipEntry(name));
            int len;
            try(FileInputStream fis = new FileInputStream(file);){
                while ((len = fis.read(bytes)) != -1){
                    zos.write(bytes, 0, len);
                }
                zos.closeEntry();
            }
        }else {
            File[] files = file.listFiles();
            if (files == null || files.length == 0){
                // 如果保留源文件结构
                if (keepDirStructure){
                    // 空文件夹进行处理
                    zos.putNextEntry(new ZipEntry(name + "/"));
                    zos.closeEntry();
                }
            }else {
                for (File f : files) {
                    // 如果保留源文件结构
                    if (keepDirStructure){
                        // 需要保留就在名称前加上斜杠
                        compress(f, zos, name + "/" + f.getName(), keepDirStructure);
                    }else {
                        // 不需要保留则直接用当前文件名，这样就会把所有文件都生产在zip的根目录下
                        compress(f, zos, file.getName(), keepDirStructure);
                    }
                }
            }
        }
    }

    @SneakyThrows
    public boolean copyOrCutFile(List<CdFile> sourceFiles, boolean isCut, String targetFolder){
        AtomicBoolean flag = new AtomicBoolean(true);
        if (targetFolder.equals(ROOT_FILE_NAME)){
            targetFolder = rootPath;
        }
        String finalTargetFolder = targetFolder;
        if (isCut){
            // 剪切
            sourceFiles.forEach( cdFile -> {
                if (cdFile.getFileStatus() == 0){
                    try {
                        FileUtils.moveFileToDirectory(new File(cdFile.getFilePath()), new File(finalTargetFolder), true);
                    } catch (IOException e) {
                        e.printStackTrace();
                        flag.set(false);
                    }
                }else {
                    try {
                        FileUtils.moveDirectoryToDirectory(new File(cdFile.getFilePath()), new File(finalTargetFolder), true);
                    } catch (IOException e) {
                        e.printStackTrace();
                        flag.set(false);
                    }
                }
            } );
        } else {
            // 复制
            sourceFiles.forEach(cdFile -> {
                // 一般文件
                if (cdFile.getFileStatus() == 0) {
                    try {
                        FileUtils.copyFileToDirectory(new File(cdFile.getFilePath()), new File(finalTargetFolder));
                    }catch (IOException e){
                        e.printStackTrace();
                        flag.set(false);
                    }
                }else {
                    // 文件夹
                    try {
                        FileUtils.copyDirectoryToDirectory(new File(cdFile.getFilePath()), new File(finalTargetFolder));
                    } catch (IOException e) {
                        e.printStackTrace();
                        flag.set(false);
                    }
                }
            } );
        }

        return flag.get();
    }

}
